﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

namespace SilverlightControlStyle.Controls
{
    public class DatePickerEx : DatePicker
    {
        #region 字段
        DatePickerTextBox _currentTextBox;
        Popup _currentPopup;
        Calendar _currentCalendar;
        ToggleButton _currentButon;
        string _dateFormat;
        string _inputText;
        #endregion

        #region 依赖属性 属性
        public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(DatePickerEx), new PropertyMetadata(false));

        public static readonly DependencyProperty DateModeProperty = DependencyProperty.Register("DateMode", typeof(CalendarMode), typeof(DatePickerEx), new PropertyMetadata(CalendarMode.Month, OnDateModeChanged));

        /// <summary>  
        /// 得到手动输入的值  
        /// </summary>  
        public string InputText
        {
            get { return _inputText ?? ""; }
            private set { _inputText = value; }
        }
        /// <summary>  
        /// 启用和关闭手动输入  
        /// </summary>  
        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            set { SetValue(IsReadOnlyProperty, value); }
        }
        /// <summary>  
        /// 输入时间类型  
        /// </summary>  
        public CalendarMode DateMode
        {
            get { return (CalendarMode)GetValue(DateModeProperty); }
            set { SetValue(DateModeProperty, value); }
        }
        #endregion

        #region 重写 私有方法
        /// <summary>
        /// 从模板获取控件 绑定事件
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _currentTextBox = GetTemplateChild("TextBox") as DatePickerTextBox;
            _currentPopup = GetTemplateChild("Popup") as Popup;
            _currentButon = GetTemplateChild("XToggleButton") as ToggleButton;

            #region 注册及绑定事件
            if (null != _currentTextBox)
            {
                SetTextMode(_currentTextBox);
                _currentTextBox.Text = SelectedDate.HasValue ? SelectedDate.Value.ToString(_dateFormat) : "";
                _currentTextBox.IsReadOnly = IsReadOnly;
                _currentTextBox.TextChanged += ((sender, e) =>
                {
                    if (IsReadOnly)
                        _currentTextBox.Text = SelectedDate.HasValue ? SelectedDate.Value.ToString(_dateFormat) : "";
                    else
                    {
                        if (_currentTextBox.Text.Trim().Length >= _dateFormat.Length)
                        {
                            DateTime timeTemp;
                            if (DateTime.TryParse(_currentTextBox.Text.Trim(), out timeTemp))
                                _currentTextBox.Text = timeTemp.ToString(_dateFormat);
                        }
                        InputText = _currentTextBox.Text;
                    }
                    SetTextMode(_currentTextBox);
                });

            }

            CalendarClosed += OnDatePickerExCalendarClosed;


            if (DateMode == CalendarMode.Month)
                return;

            if (null != _currentPopup)
                //利用扩展方法找到Calendar控件  
                _currentCalendar = _currentPopup.Child.Descendents().OfType<Calendar>().FirstOrDefault();


            if (null != _currentButon)
                _currentButon.Click += ((sender, e) =>
                {
                    if (_currentButon.IsChecked != null)
                        _currentPopup.IsOpen = _currentButon.IsChecked.Value;
                });


            if (null != _currentCalendar)
            {

                _currentCalendar.IsTodayHighlighted = true;
                _currentCalendar.DisplayModeChanged += OnCurrentCalendarDisplayModeChanged;
                _currentCalendar.DisplayMode = DateMode;
                _currentCalendar.LostMouseCapture += ((sender, e) =>
                {
                    SelectedDate = DisplayDate;
                });

            }
            #endregion
        }

        /// <summary>
        /// 日历显示模式变更事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnCurrentCalendarDisplayModeChanged(object sender, CalendarModeChangedEventArgs e)
        {
            _currentCalendar.DisplayModeChanged -= OnCurrentCalendarDisplayModeChanged;
            _currentCalendar.DisplayModeChanged += OnCurrentCalendarDisplayModeChanged;
            var cal = (Calendar)sender;

            //首次加载以及重新赋值DisplayMode  Calendar视图情况判断  
            if (e.OldMode.Equals(CalendarMode.Year) && !e.NewMode.Equals(CalendarMode.Month))
                cal.DisplayMode = e.NewMode;
            else
                cal.DisplayMode = DateMode;


            //仅选择月 Calendar关闭情况判断  
            if (e.NewMode.Equals(CalendarMode.Month))
            {
                _currentPopup.IsOpen = false;
                _currentButon.IsChecked = false;
            }

            //只选择年。 Calendar 关闭情况判断  
            if (DateMode == CalendarMode.Decade && e.NewMode == CalendarMode.Year && e.OldMode == DateMode)
            {
                _currentPopup.IsOpen = false;
                _currentButon.IsChecked = false;
            }
        }
        /// <summary>  
        /// 设置日期显示格式  
        /// </summary>  
        /// <param name="tbx"></param>  
        protected void SetTextMode(DatePickerTextBox tbx)
        {
            if (null == tbx)
                return;
            switch (DateMode)
            {
                case CalendarMode.Year:
                    _dateFormat = "yyyy.MM";
                    tbx.Watermark = "<yyyy.MM>";
                    break;
                case CalendarMode.Decade:
                    _dateFormat = "yyyy";
                    tbx.Watermark = "<yyyy>";
                    break;
                default:
                    _dateFormat = "yyyy.MM.dd";
                    break;
            }
            tbx.UpdateLayout();
        }
        #endregion

        #region 自定义事件
        /// <summary>
        /// 键盘按键抬起事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            if (!IsReadOnly)
                InputText = _currentTextBox.Text;
        }
        /// <summary>
        /// 日期选择控件关闭事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnDatePickerExCalendarClosed(object sender, RoutedEventArgs e)
        {
            if (null != SelectedDate)
                Text = SelectedDate.Value.ToString(_dateFormat);
            InputText = Text;
        }
        /// <summary>
        /// 日历显示模式变更事件
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        protected static void OnDateModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as DatePickerEx;
            if (sender != null && null != sender._currentCalendar)
                sender._currentCalendar.DisplayMode = (CalendarMode)e.NewValue;
            if (sender != null) sender.SetTextMode(sender._currentTextBox);
        }
        #endregion
    }

    public static class ControlsHelper
    {
        /// <summary>  
        /// 扩展DependencyObject Descendents方法，递归找到制定的控件  
        /// </summary>  
        /// <param name="root"></param>  
        /// <returns></returns>  
        public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
        {
            int count = VisualTreeHelper.GetChildrenCount(root);
            for (int i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(root, i);
                yield return child;
                foreach (var descendent in Descendents(child))
                    yield return descendent;
            }
        }
    }
}
